/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package czStvDpds.graphics3D;

import czStvDpds.logging.Logging;
import java.io.File;
import java.io.FileInputStream;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.logging.Level;
import org.lwjgl.BufferUtils;

/**
 * Třída slouží pro nahrání dat modelu ze souboru.
 *
 * @author divad
 */
public class TMDLoader {

    private FloatBuffer meshData;
    private IntBuffer indices;
    private File sourceFile;
    private boolean hasMaterialInfo = false;
    private File materialInfo;
    private int texCoordsNumber = 0;
    public static final int NOT_LOADED = 1, FULLY_LOADED = 2, PARTIALLY_LOADED = 3;
    private int state = NOT_LOADED;

    /**
     * Pokusí se načíst objekt. Pokud jsou data požkozena, načte aspoň to, co se
     * podaří přečíst. Pokud již byl načten jiný objekt, přemaže ho. (Uvnitř
     * sebe). Nenahrává materiál!!! pouze najde jeho soubor.
     *
     * @param tmdFile
     */
    public void loadFile(File tmdFile) {
        this.meshData = null;
        this.indices = null;
        this.sourceFile = null;
        this.hasMaterialInfo = false;
        this.materialInfo = null;
        this.texCoordsNumber = 0;
        if (tmdFile != null & tmdFile.getPath().substring(tmdFile.getPath().lastIndexOf('.')).compareTo(".tmd") == 0) {
            sourceFile = tmdFile;
            try {
                String data = this.loadData(tmdFile);
                if (data != null) {
                    data = this.removeComments(data);
                    data.replace("\r", "");
                    String[] parts = this.separateParts(data);
                    for (int i = 0; i < parts.length; i++) {
                        String[] lines = separateLines(parts[i]);
                        this.getData(lines);
                    }
                    if (this.indices == null || this.meshData == null || this.hasMaterialInfo == false) {
                        this.state = TMDLoader.PARTIALLY_LOADED;
                    } else {
                        this.state = TMDLoader.FULLY_LOADED;
                    }
                }
            } catch (Exception ex) {
                Logging.getMainLogger().log(Level.WARNING, "File can not be readed: " + tmdFile.getPath(), ex);
                this.state = TMDLoader.NOT_LOADED;
            }

        } else {
            this.state = TMDLoader.NOT_LOADED;
        }
    }

    /**
     * Vrací true, pokud se existuje soubor s informací o materiálu moedlu.
     *
     * @return
     */
    public boolean hasMaterialInfo() {
        return hasMaterialInfo;
    }

    /**
     * Vrací načtené indexi vertexů. Pokud se indexi nepodařilo přečíst, vrací
     * null.
     *
     * @return
     */
    public IntBuffer getIndices() {
        return indices;
    }

    /**
     * Vrací soubor s informacemi o materiálu modelu, pokud existuje, jinak
     * vrací null.
     *
     * @return
     */
    public File getMaterialInfo() {
        return materialInfo;
    }

    /**
     * Vrací načtené vertexy modelu. Pokud se je nepodařilo přečíst, vrací null.
     *
     * @return
     */
    public FloatBuffer getMeshData() {
        return meshData;
    }

    public int getTexCoordsNumber() {
        return texCoordsNumber;
    }

    public File getSourceFile() {
        return sourceFile;
    }

    /**
     * Vrátí stav nahrání souboru - TMDLoader.NOT_LOADED, FULLY_LOADED a
     * PARTIALLY_LOADED
     *
     * @return
     */
    public int getState() {
        return state;
    }
    ///////////////////nahrávací a dekódovací funkce/////////////////////////

    private void getData(String[] lines) throws Exception {
        if (lines[0].compareTo("DATA") == 0) {
            this.meshData = this.readMeshData(lines);
        } else if (lines[0].compareTo("INDICES") == 0) {
            if (lines.length == 2) {
                this.indices = this.readMeshIndices(lines[1]);
            } else if (lines.length == 1) {
                this.indices = BufferUtils.createIntBuffer(0);
            }
        } else if (lines[0].compareTo("MATERIAL") == 0) {
            if (lines.length == 2) {
                this.materialInfo = this.getMaterialFile(lines[1]);
                this.hasMaterialInfo = this.materialInfo.exists();
            }
        }
    }

    private File getMaterialFile(String line) {
        File f = null;
        line = line.substring(0, line.lastIndexOf(';'));
        if (line.indexOf('/') == -1) {
            String originalPath = this.sourceFile.getPath();
            f = new File(originalPath.substring(0, originalPath.lastIndexOf(System.getProperty("file.separator"))) + System.getProperty("file.separator") + line);
        } else {
            f = new File(line);
        }
        return f;
    }

    private IntBuffer readMeshIndices(String line) {
        ArrayList<Integer> indices = new ArrayList(50);
        int sPoz = 0;
        int kPoz = line.indexOf(';');
        while (sPoz >= 0 && kPoz > 0) {
            indices.add(Integer.parseInt(line.substring(sPoz, kPoz)));
            sPoz = kPoz + 1;
            kPoz = line.indexOf(';', sPoz);
        }
        return this.intsToBuffer(indices.toArray(new Integer[]{}));
    }

    private IntBuffer intsToBuffer(Integer[] ints) {
        IntBuffer data = BufferUtils.createIntBuffer(ints.length);
        for (int i = 0; i < ints.length; i++) {
            data.put(ints[i].intValue());
        }
        data.rewind();
        return data;
    }

    private FloatBuffer readMeshData(String[] partLines) throws Exception {
        ArrayList<Float> data = new ArrayList(50);
        int valuesNumber = 0;
        for (int i = 1; i < partLines.length; i++) {
            int localValuesNumber = 0;
            String line = partLines[i];
            int sPoz = 0;
            int kPoz = line.indexOf(';');
            while (sPoz >= 0 && kPoz > 0) {
                data.add(Float.parseFloat(line.substring(sPoz, kPoz)));
                sPoz = kPoz + 1;
                kPoz = line.indexOf(';', sPoz);
                localValuesNumber++;
            }
            if (i == 1) {
                valuesNumber = localValuesNumber;
                this.texCoordsNumber = (valuesNumber - 6) / 3;
            } else {
                if (valuesNumber != localValuesNumber) {
                    this.texCoordsNumber = 0;
                    throw new Exception("Wrong data format");
                }
            }
            localValuesNumber = 0;
        }
        return this.floatsToBuffer(data.toArray(new Float[]{}));
    }

    private FloatBuffer floatsToBuffer(Float[] floats) {
        FloatBuffer data = BufferUtils.createFloatBuffer(floats.length);
        for (int i = 0; i < floats.length; i++) {
            data.put(floats[i].floatValue());
        }
        data.rewind();
        return data;
    }

    private Float[] readLineMeshData(String line) {
        ArrayList<Float> floats = new ArrayList(15);
        int sPoz = 0;
        int kPoz = line.indexOf(';');
        while (sPoz >= 0 && kPoz > 0) {
            floats.add(Float.parseFloat(line.substring(sPoz, kPoz)));
            sPoz = kPoz + 1;
            kPoz = line.indexOf(';', sPoz);
        }
        return floats.toArray(new Float[]{});
    }

    private String[] separateLines(String part) {
        ArrayList<String> lines = new ArrayList(20);
        int sPoz = 0;
        int kPoz = part.indexOf('\n', sPoz + 1);
        while (sPoz >= 0 && kPoz > 0) {
            lines.add(part.substring(sPoz, kPoz - 1));
            sPoz = kPoz + 1;
            kPoz = part.indexOf('\n', sPoz);
        }
        return lines.toArray(new String[]{});
    }

    private String removeComments(String data) {
        boolean continueReading = true;
        while (continueReading) {
            int poziceComentu = data.indexOf("//");
            if (poziceComentu >= 0) {
                int poziceKonceComentu = data.indexOf('\n', poziceComentu);
                data = data.substring(0, poziceComentu) + data.substring(poziceKonceComentu + 1, data.length());
            } else {
                continueReading = false;
            }

        }
        return data;
    }

    private String[] separateParts(String data) {
        ArrayList<String> parts = new ArrayList(5);
        int sPoz = data.indexOf('$') + 1;
        int kPoz = data.indexOf('$', sPoz + 1);
        while (sPoz >= 0 && sPoz < data.length() && kPoz >= 0) {
            parts.add(data.substring(sPoz, kPoz));
            sPoz = kPoz + 1;
            kPoz = data.indexOf('$', sPoz);
        }
        return parts.toArray(new String[]{});
    }

    private String loadData(File f) throws Exception {
        FileInputStream fis = new FileInputStream(f);
        byte[] byteData = new byte[fis.available()];
        fis.read(byteData);
        String data = new String(byteData);
        return data;
    }
}
